﻿using System;
using System.Data;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data.Sql;
using Simulator;
using Statistics;


namespace DatabaseRW
{

    /// <summary>
    /// Класс для чтения из базы данных и записи в базу данных
    /// </summary>
    public class DBRW
    {
        private const int NParams = 10;//10 параметров может быть у ресурса. Столько же соотв-х столбцов в базе данных
        private const int NCoefs = 5;//5 - максимальное количество коэффициентов мат. выражения.
                                    //Столько же соотв-х столбцов в базе данных

        public SqlConnection myConnection = new SqlConnection();
        private SqlConnection servConnection = new SqlConnection();
        /// <summary>
        /// Подключиться к базе данных
        /// </summary>
        /// <param name="ServerName">Имя сервера</param>
        /// <param name="DatabaseName">Название базы данных</param>
        /// <returns>true, если подключено успешно, иначе false </returns>
        public bool ConnectToDB(string ServerName, string DatabaseName)
        {
            bool Success = true;
            if (myConnection.State == ConnectionState.Open)
                myConnection.Close();//если уже подключены к другой БД, отключимся

            string str = String.Format("Server = " + ServerName + ";" +
                                      "Trusted_Connection = true;" +
                                      "Database = " + DatabaseName + ";" +
                                      "Connection Timeout = 10");//строка подключения к БД

            myConnection.ConnectionString = str;
            try
            {
                myConnection.Open();
            }
            catch
            {
                Success = false;
            }
            return (Success);
        }

        /// <summary>
        /// Подключение к серверу
        /// </summary>
        /// <returns>true, если подключено успешно, иначе false </returns>
        public bool ConnectToServer(string ServerName)
        {
            try
            {
                SqlConnectionStringBuilder conStr = new SqlConnectionStringBuilder();
                conStr.DataSource = @ServerName;
                conStr.IntegratedSecurity = true;

                if (servConnection.State == ConnectionState.Open)
                    servConnection.Close();//если уже подключены к другому серверу, отключимся

                servConnection.ConnectionString = conStr.ToString();
                servConnection.Open();

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        /// <summary>
        /// Получение списка доступных серверов
        /// </summary>
        /// <returns> Список серверов в локальной сети </returns>
        public List<string> GetServersList()
        {
            List<string> curServerList = new List<string>();
            curServerList.Clear();
            curServerList.Add("(local)");//имя сервера на локальном компьтере
            string item = "";
            try
            {
                //получение информации о доступных серверах
                SqlDataSourceEnumerator instance = SqlDataSourceEnumerator.Instance;
                DataTable dataTable = instance.GetDataSources();
                foreach (DataRow row in dataTable.Rows)//чтение имен серверов
                {
                    item = "";
                    if (row[1].ToString().Length != 0)
                    {
                        item = @"\" + row[1].ToString();
                    }
                    item = row[0] + item;
                    //curServerList.Add(row["ServerName"].ToString());
                    curServerList.Add(item);
                }
            }
            catch (Exception)
            {

            }
            return curServerList;
        }

        /// <summary>
        /// Получение списка БД конкретного сервера
        /// </summary>
        /// <param name="ServerName">Имя сервера</param>
        /// <returns>Список БД, хранимых на сервере</returns>
        public List<string> GetDbList(string ServerName)
        {
            try
            {
                if (ConnectToServer(ServerName))//если подключение к серверу прошло успешно
                {
                    string query = "SELECT name FROM sys.databases";//запрос на вывод списка имен БД
                    DataTable table = SaveResRequest(query, servConnection);//сохранение результата запроса в таблице
                    //из полученной таблицы считываем все имена баз данных на конкретном сервере
                    List<string> list = FromTableToList(table);
                    return list;
                }
                else return null;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// Получение списка таблиц БД
        /// </summary>
        /// <returns>Список названий таблиц БД</returns>
        public List<string> GetTableList()
        {
            try
            {
                string query = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";//запрос на вывод списка имен БД
                DataTable table = SaveResRequest(query, myConnection);//сохранение результата запроса в таблице
                //из полученной таблицы считываем все имена таблиц БД
                List<string> list = FromTableToList(table);
                return list;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// Получение списка полей таблицы
        /// </summary>
        /// <param name="NameTable">Название таблицы</param>
        /// <returns>Список полей таблицы</returns>
        public DataTable GetColumnList(string NameTable)
        {
            string str = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + NameTable + "'";
            DataTable table = SaveResRequest(str, myConnection);
            return table;
        }

        /// <summary>
        /// Сохранение результата запроса в таблице
        /// </summary>
        /// <param name="Request">Запрос</param>
        /// <returns>Таблица с результатом запроса</returns>
        private DataTable SaveResRequest(string Request, SqlConnection conect)
        {
            DataTable table = new DataTable("TableList");
            using (SqlDataAdapter adapter = new SqlDataAdapter(Request, conect))
            {   //выполнение запроса
                adapter.Fill(table);//сохранение результата запроса в таблице
                return table;
            }
        }

        /// <summary>
        /// Чтение таблицы (Результат запроса в виде списка)
        /// </summary>
        /// <param name="table">Таблица</param>
        /// <returns>Список значений из таблицы</returns>
        public List<string> FromTableToList(DataTable table)
        {
            List<string> list = new List<string>();
            for (int i = 0; i < table.Rows.Count; i++)
            {
                list.Add(table.Rows[i][0].ToString());
            }
            return list;
        }

        /// <summary>
        /// Создание структутры БД на конкретном сервере
        /// </summary>
        /// <param name="ServerName">Имя сервера</param>
        /// <param name="DBName">Имя новой БД</param>
        /// <returns>true, если операция успешно завершена, иначе false</returns>
        public bool CreationDataBase(string ServerName, string DBName)
        {
            try
            {
                if (ConnectToServer(ServerName))//если подключение к серверу прошло успешно
                {
                    string str = "CREATE DATABASE " + DBName;
                    Command(str, servConnection);
                    ConnectToDB(ServerName, DBName);
                    AddTableCategory();
                    AddTableParamNames();
                    AddTableActions();
                    return true;
                }
                else
                    return false;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Выполнение запроса
        /// </summary>
        /// <param name="str">текст запроса</param>
        void Command(string str, SqlConnection conect)
        {
            SqlCommand myCommand = new SqlCommand(str, conect);
            myCommand.ExecuteNonQuery();
        }

        /// <summary>
        /// Удаление БД
        /// </summary>
        /// <param name="ServerName">Имя сервера</param>
        /// <param name="DBName">Имя удаляемой БД</param>
        /// <returns>true, если операция успешно завершена, иначе false</returns>
        public bool DeleteDataBase(string ServerName, string DBName)
        {
            try
            {
                if (ConnectToServer(ServerName))//если подключение к серверу прошло успешно
                {
                    string sstr = "ALTER DATABASE " + DBName + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
                    Command(sstr, servConnection);
                    string str = "DROP DATABASE " + DBName;
                    Command(str, servConnection);
                    servConnection.Close();
                    return true;
                }
                else
                    return false;
            }
            catch (Exception)
            {
                return false;
            }
        }

        #region Создание таблиц
        /// <summary>
        /// Создание таблицы Категория
        /// </summary>
        private void AddTableCategory()
        {
            string str = "CREATE TABLE Category (id int PRIMARY KEY, Name text NOT NULL, TableName text NOT NULL)";
            Command(str, myConnection);
        }

        /// <summary>
        /// Создание таблицы для ресурсов определенной категории
        /// </summary>
        /// <param name="TableName">Название таблицы из столбца TableName, соответствующее категории ресурса</param>
        public void AddTableTableName(string TableName)
        {
            string str = "CREATE TABLE " + TableName + " (id int PRIMARY KEY,Name text NOT NULL, Category text NOT NULL";
            for (int i = 0; i < 10; i++)
            {
                str += ", Param" + i + " float(5) NOT NULL";
            }
            str += ")";
            Command(str, myConnection);
        }

        /// <summary>
        /// Создание таблицы параметров
        /// </summary>
        private void AddTableParamNames()
        {
            string str = "CREATE TABLE ParamNames (id int PRIMARY KEY, Name text NOT NULL)";
            Command(str, myConnection);
        }

        /// <summary>
        /// Создание таблицы статистических данных
        /// </summary>
        private void AddTableActions()
        {
            string str = "CREATE TABLE Actions (id int PRIMARY KEY, Name text NOT NULL, DistribT text NOT NULL, " +
                "DistribQ text NOT NULL, MathStringT text NOT NULL, MathStringQ text NOT NULL";
            for (int i = 0; i < 5; i++)
            {
                str += ", CoefsQ" + i + " int NOT NULL";
            }
            for (int i = 0; i < 5; i++)
            {
                str += ", CoefsT" + i + " int NOT NULL";
            }
            str += ")";
            Command(str, myConnection);
        }
        #endregion

        /// <summary>
        /// Проверка на соответствие стандарту БД
        /// </summary>
        /// <returns>true, если БД соответствует стандарту, иначе false</returns>
        public bool CheckConnet()
        {
            List<string> list = GetTableList();
            string ColumnName1 = list.Find((x) => x == "Category");
            string ColumnName2 = list.Find((x) => x == "ParamNames");
            string ColumnName3 = list.Find((x) => x == "Actions");
            if (ColumnName1 == null || ColumnName2 == null || ColumnName3 == null)
                return false;
            else
                return true;
        }

        /// <summary>
        /// Просмотр таблицы
        /// </summary>
        /// <param name="Name">Имя таблицы</param>
        /// <returns>Таблица</returns>
        public DataTable ViewTable(string Name)
        {
            string str = "SELECT * FROM" + Name;
            DataTable table = SaveResRequest(str, myConnection);//сохранение результата запроса в таблице
            return table;
        }

        /// <summary>
        /// Удаление таблицы из БД
        /// </summary>
        /// <param name="TableName">Имя таблицы</param>
        public void DeleteTable(string TableName)
        {
            string str = "DROP TABLE " + TableName;
            Command(str, myConnection);
        }

        /// <summary>
        /// Прочитать данные из открытой базы данных
        /// </summary>
        /// <param name="ParamNames">Список названий параметров ресурса (кроме стоимости)</param>
        /// <param name="actions">Список операций с известными характеристиками, занесенными в базу данных</param>
        public bool ReadDataBase(ref List<string> ParamNames, ref SortedList<string,List<AtomicRes>> Categories, ref List<ActionData> actions)
        {
            try
            {
                //прочитать базу данных

                SqlCommand myCommand;
                SqlDataReader reader;

                //Определим список таблиц ресурсов в базе данных
                //Каждая тарблица ресурсов соответствует 1-й катерогии
                List<string> tableList = new List<string>();

                myCommand = new SqlCommand("SELECT * FROM Category", myConnection);//из таблицы Category
                reader = myCommand.ExecuteReader();
                if (reader.HasRows)
                {
                    while (reader.Read())
                    {
                        //новая категория
                        Categories.Add(Convert.ToString(reader["Name"]), new List<AtomicRes>());
                        tableList.Add(Convert.ToString(reader["TableName"]));
                    }
                }
                reader.Close();
                //Определили список таблиц ресурсов. Прочитаем отдельные таблицы  для каждого ресурса
                foreach (string tn in tableList)
                {
                    myCommand = new SqlCommand("SELECT * FROM " + tn, myConnection);//из таблицы tn
                    reader = myCommand.ExecuteReader();
                    if (reader.HasRows)
                    {
                        AtomicRes ar = new AtomicRes();
                        //для каждой строки - свой атомарный ресурс

                        while (reader.Read())
                        {
                            ar.Name = Convert.ToString(reader["Name"]);
                            ar.Category = Convert.ToString(reader["Category"]);
                            for (int i = 0; i < NParams; i++)
                            {
                                ar.Params.Add(Convert.ToDouble(reader["Param" + Convert.ToString(i)]));
                            }
                        }
                        Categories[ar.Category].Add(ar);//добавим атомарный ресурс в нужную категорию
                    }
                    reader.Close();
                }
                //Теперь в списке Categories - все описанные в БД ресурсы
                //Прочитаем названия параметров

                myCommand = new SqlCommand("SELECT * FROM ParamNames", myConnection);//из таблицы ParamNames
                reader = myCommand.ExecuteReader();
                if (reader.HasRows)
                {
                    while (reader.Read())
                    {
                        ParamNames.Add(Convert.ToString(reader["Name"]));
                    }
                }
                reader.Close();
                //В заключение прочитаем статистические данные насчет операций

                myCommand = new SqlCommand("SELECT * FROM Actions", myConnection);//из таблицы Actions
                reader = myCommand.ExecuteReader();
                if (reader.HasRows)
                {
                    while (reader.Read())
                    {
                        ActionData ad = new ActionData();
                        ad.Name = Convert.ToString(reader["Name"]);
                        /* switch(Convert.ToString(reader["DistribT"]))
                         {
                             case "Normal":
                                 ad.DistribT = Distribution.Normal;
                                 break;
                             case "Exponential":
                                 ad.DistribT = Distribution.Exponential;
                                 break;
                             case "Uniform":
                                 ad.DistribT = Distribution.Uniform;
                                 break;
                             default:
                                 //добавить код сюда
                                 break;
                         }
                         switch(Convert.ToString(reader["DistribQ"]))
                         {
                             case "Normal":
                                 ad.DistribQ = Distribution.Normal;
                                 break;
                             case "Exponential":
                                 ad.DistribQ = Distribution.Exponential;
                                 break;
                             case "Uniform":
                                 ad.DistribQ = Distribution.Uniform;
                                 break;
                             default:
                                 //добавить код сюда
                                 break;
                         }*/
                        //прочитаем матем. выр-я
                        ad.TimeString = Convert.ToString(reader["MathStringT"]);
                        ad.QualityString = Convert.ToString(reader["MathStringQ"]);
                        /*for (int i = 0; i < NCoefs; i++)
                        {
                            ad.CoefsQ.Add(Convert.ToInt32(reader["CoefsQ" + Convert.ToString(i)]));
                        }
                        for (int i = 0; i < NCoefs; i++)
                        {
                            ad.CoefsT.Add(Convert.ToInt32(reader["CoefsT" + Convert.ToString(i)]));
                        }*/
                        actions.Add(ad);


                    }
                }
                reader.Close();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        /// <summary>
        /// Завершить соединение с базой данных
        /// </summary>
        void Disconnect()
        {
            myConnection.Close();
        }

    }
}
